package com.winit.menu.service;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.winit.common.base.BaseController;
import com.winit.common.base.ExecuteResult;
import com.winit.common.util.Constants;
import com.winit.common.util.ListUtil;
import com.winit.commons.util.HttpClientUtil;
import com.winit.commons.util.NumberUtil;
import com.winit.commons.util.ObjectToString;
import com.winit.feature.manager.PathContainer;
import com.winit.menu.model.MatrixMenu;
import com.winit.menu.repository.MatrixMenuOperations;
import com.winit.menu.repository.MatrixMenuRepository;
import com.winit.menu.util.MenuUtil;
import com.winit.page.service.IPageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.util.*;

/**
 * Created by wangjian on 2017/10/27.
 */
@Component
public class MatrixMenuService implements IMenuService {

    private static Logger logger = LoggerFactory.getLogger(MatrixMenuService.class);

    @Autowired
    MatrixMenuOperations operations;

    @Autowired
    MatrixMenuRepository repository;
    @Value("${url.wsAgent.menu.recreate}")
    private String wsAgentMenuRecreateUrl;
    @Autowired
    private IPageService pageService;
    @Autowired
    private PathContainer pathContainer;


    public MatrixMenu initAddRootMenu(String sys) {
        MatrixMenu menu = new MatrixMenu();
        menu.setId(MenuUtil.createMenuId());
//        menu.setMenu_id("ROOT-" + sys.toUpperCase());
        menu.setLevel(Constants.MENU_LEVEL_0);
        menu.setSys(sys);
        menu.setParent_id(Constants.MENU_ROOT_PARENT_ID);
        menu.setStatus(Constants.MENU_STATUS_ONLINE);
        menu.setIs_del(Constants.MENU_IS_DELETED_NO);
        menu.setReq_id(Constants.MENU_ROOT_REQ_ID);
        operations.updateMenu(menu);
        return menu;
    }

//    public boolean validate(String menu_id, String parent_id) {
//        List<MatrixMenu> list = repository.findAllByMenu_idAndParent_id(menu_id, parent_id);
//        if (list.size() == 0) {
//            return true;
//        }
//        return false;
//    }

    public MatrixMenu saveMenu(MatrixMenu menu) {
        if (Constants.MENU_STATUS_DELETE.equalsIgnoreCase(menu.getStatus())) {
            //删除节点
//            MatrixMenu m = repository.findById(menu.getId());
//            Assert.isTrue(null != m, "查询不到menu." + menu.getId());
            //新增的节点直接删除
//            if(Constants.MENU_STATUS_ADD.equalsIgnoreCase(m.getStatus())){
//                MatrixMenu parent = repository.findById(m.getParent_id());
//                Assert.isTrue(null != parent, "查询不到父级menu." + m.getParent_id());
//                parent.getChildren().remove(m.getId());
//                operations.deleteMenu(m);
//                operations.updateMenu(parent);
//                return m;
//            }
//            m.setStatus(Constants.MENU_STATUS_DELETE);
//            m.setReq_id(menu.getReq_id());
//            operations.updateMenu(m);
//            return m;
            return this.deleteMenuById(menu.getId(), false);
        }
        //1、status 为ADD时新增 新增的时候需要考虑req
        if (Constants.MENU_STATUS_ADD.equalsIgnoreCase(menu.getStatus())) {
            return addMenu(menu);
        }

        if (Constants.MENU_STATUS_UPDATE.equalsIgnoreCase(menu.getStatus())) {
            //直接更新menu
            MatrixMenu m = repository.findById(menu.getId());
            Assert.isTrue(null != m, "查询不到menu." + menu.getId());
//            m.setName(menu.getName());
//            m.setRightId(menu.getRightId());
//            operations.updateMenu(m);
            operations.updateByIdForPropertiesNotNull(menu);
            m = repository.findById(menu.getId());
            if ((!StringUtils.isEmpty(menu.getMenu_name())) || (!StringUtils.isEmpty(menu.getParent_id()))) {
                this.pathContainer.updatePath(m.getId(), m.getParent_id(), m.getMenu_name(), m.getChildren());
            }
            return m;
        }
        Assert.isTrue(false, "参数错误status." + ObjectToString.getString(menu));
        return null;
    }

    public MatrixMenu deleteMenuById(String menuId, boolean deleteAllPageFeature) {
        MatrixMenu m = repository.findById(menuId);
        Assert.isTrue(null != m, "查询不到menu." + menuId);
        //新增的节点直接删除
//            if(Constants.MENU_STATUS_ADD.equalsIgnoreCase(m.getStatus())){
        MatrixMenu parent = repository.findById(m.getParent_id());
        Assert.isTrue(null != parent, "查询不到父级menu." + m.getParent_id());
        parent.getChildren().remove(m.getId());
        operations.updateMenu(parent);
        this.deleteMenu(menuId, true, deleteAllPageFeature);
        this.pathContainer.remove(menuId);
        this.pathContainer.updatePath(parent.getId(), parent.getParent_id(), parent.getMenu_name(), parent.getChildren());
        return m;
//            }
    }

    private void deleteMenu(String menuId, boolean recursive, boolean deleteAllPageFeature) {
        MatrixMenu m = repository.findById(menuId);
        operations.deleteMenu(m);
//        if ("3".equals(m.getLevel())) {
        ExecuteResult executeResult = this.pageService.deleteAllMenuPage(menuId, deleteAllPageFeature);
        Assert.isTrue(executeResult.isSuccess(), executeResult.getMsg());
//        } else {
        if (!recursive) {
            return;
        }
        List<String> children = m.getChildren();
        if (CollectionUtils.isEmpty(children)) {
            return;
        }
        for (String childId : children) {
            this.deleteMenu(childId, recursive, deleteAllPageFeature);
        }
//        }
    }


    private MatrixMenu addMenu(MatrixMenu menu) {
        Assert.hasText(menu.getLevel(), "新增menu，level不能为空");
        Assert.hasText(menu.getSys(), "新增menu，sys不能为空");
//        Assert.hasText(menu.getMenu_id(), "新增menu，menu id不能为空");
        Assert.hasText(menu.getParent_id(), "新增menu，parent id不能为空");

//        boolean flag = validate(menu.getMenu_id(), menu.getParent_id());
//        Assert.isTrue(flag, "新增menu，menu id 已存在");

        menu.setId(MenuUtil.createMenuId());
        MatrixMenu parent2 = repository.findById(menu.getParent_id());
        Assert.isTrue(null != parent2, "查询父节点为null . parent id=" + menu.getParent_id());

//        boolean withLevel = false;
//        MatrixMenu parent1 = null;
        menu.setIs_del(Constants.MENU_IS_DELETED_NO);
        if (parent2.getChildren() == null) {
            List<String> list = new ArrayList<>();
            list.add(menu.getId());
            parent2.setChildren(list);
        } else {
            parent2.getChildren().add(menu.getId());
        }
        try {
            NumberUtil.SysEnum sysEnum = null;
            String sys = parent2.getSys();
            if ("tom".equalsIgnoreCase(sys)) {
                sysEnum = NumberUtil.SysEnum.TOM;
            } else if ("sellerportal".equalsIgnoreCase(sys)) {
                sysEnum = NumberUtil.SysEnum.OPC;
            } else if ("matrix".equalsIgnoreCase(sys)) {
                sysEnum = NumberUtil.SysEnum.MATRIX;
            } else {
                logger.error("unsupported sys for:" + sys);
                return null;
            }
            menu.setMenuNum(NumberUtil.createNumber(sysEnum, parent2.getMenuNum(), NumberUtil.TypeEnum.MENU, parent2.getChildren().size()));
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        }
        operations.updateMenu(menu);
        operations.updateMenu(parent2);
//        if (withLevel) {
//            operations.updateMenu(parent1);
//        }
        this.pathContainer.add(menu.getId(), menu.getParent_id(), menu.getMenu_name(), menu.getChildren());
        this.pathContainer.updatePath(parent2.getId(), parent2.getParent_id(), parent2.getMenu_name(), parent2.getChildren());
        return menu;
    }


    public ExecuteResult createRootMenu(String sys) {
        ExecuteResult executeResult = new ExecuteResult();
        if (!sys.matches("[a-zA-Z]+")) {
            executeResult.setToError();
            executeResult.setMsg("sys只能由英文字母组成");
            return executeResult;
        }
        sys = sys.toUpperCase();
        List<MatrixMenu> exists = this.repository.findBySys(sys);
        if (!CollectionUtils.isEmpty(exists)) {
            executeResult.setToError();
            executeResult.setMsg("指定的sys已经存在root节点");
            return executeResult;
        }
        MatrixMenu root = new MatrixMenu();
        root.setId(MenuUtil.createMenuId());
        root.setSys(sys);
        root.setMenuNum("ROOT-" + sys);
        root.setMenu_name("ROOT-" + sys);
        root.setParent_id("0");
        root.setIs_del(Constants.MENU_IS_DELETED_NO);
        root.setLevel("0");
        root.setReq_id("-1");
        root.setStatus("ONLINE");
        Map<String, String> menuData = new HashMap<>();
        menuData.put("name", root.getMenu_name());
        menuData.put("path", root.getMenuNum());
        menuData.put("routeId", "/" + root.getMenuNum());
        root.setMenu_data(menuData);
        operations.updateMenu(root);
//        if (withLevel) {
//            operations.updateMenu(parent1);
//        }
        executeResult.setData(root);
        return executeResult;
    }


//    public List<MatrixMenu> getMenuForWsAgent(List<String> reqIds, String id, String sys){
//        List<MatrixMenu> rs = new ArrayList<>();
//        MatrixMenu rawMenu = this.getMenu(id, sys);
//        if (null == rawMenu){
//            return rs;
//        }
//        this.dealMenuForWsAgent(rawMenu, reqIds);
//        if ("0".equalsIgnoreCase(rawMenu.getParent_id())){
//            rs = rawMenu.getChild();
//        }
//        return rs;
//    }

//    private void dealMenuForWsAgent(MatrixMenu matrixMenu, String reqId){
//        if (StringUtils.isEmpty(reqId)){
//            reqId = "-1";
//        }
//        List<MatrixMenu> children = matrixMenu.getChild();
//        if (!CollectionUtils.isEmpty(children)) {
//            Iterator<MatrixMenu> iterator = children.iterator();
//            while (iterator.hasNext()){
//                MatrixMenu child = iterator.next();
//                boolean validChild = true;
//                if (Constants.FEATURE_STATUS_ADD.equalsIgnoreCase(child.getStatus()) && (!reqId.equalsIgnoreCase(child.getReq_id()))){
//                    iterator.remove();
//                    validChild = false;
//                }
//                if (Constants.FEATURE_STATUS_DELETE.equalsIgnoreCase(child.getStatus()) && (reqId.equalsIgnoreCase(child.getReq_id()))){
//                    iterator.remove();
//                    validChild = false;
//                }
//                if (validChild){
//                    this.dealMenuForWsAgent(child, reqId);
//                }
//            }
//        }
//    }

    public ExecuteResult applyMenuUpdate(String menuId, String reqId, String menuSys) {
        ExecuteResult executeResult = new ExecuteResult();
        List<MatrixMenu> matrixMenus = this.getMenuForWsAgent(null, null, menuSys);
        if (CollectionUtils.isEmpty(matrixMenus)) {
            return executeResult;
        }
        List<Map<String, Object>> menuItems = this.adapteToWsParams(matrixMenus);
        try {
            Map<String, String> params = new HashMap<>();
            params.put("workspace", "default");
            params.put("menu_items", JSONObject.toJSONString(menuItems));
            String rs = HttpClientUtil.doPost(wsAgentMenuRecreateUrl, JSONObject.toJSONString(params));
            Map map = (Map) JSONObject.parse(rs);
            executeResult.setMsg((String) map.get("msg"));
            if (!(Boolean) map.get("isSuccess")) {
                executeResult.setCode(BaseController.REQUEST_ERROR);
            }
        } catch (Exception e) {
            executeResult.setCode(BaseController.REQUEST_ERROR);
            executeResult.setMsg(e.getMessage());
        }
        return executeResult;
    }

    private List<Map<String, Object>> adapteToWsParams(List<MatrixMenu> matrixMenus) {
        if (CollectionUtils.isEmpty(matrixMenus)) {
            return null;
        }
        List<Map<String, Object>> rs = new ArrayList<Map<String, Object>>();
        Map<String, Object> temp = null;
        for (MatrixMenu matrixMenu : matrixMenus) {
            temp = this.adapteToWsParams(matrixMenu);
            if (!CollectionUtils.isEmpty(temp)) {
                rs.add(temp);
            }
        }
        return rs;
    }

    private Map<String, Object> adapteToWsParams(MatrixMenu matrixMenu) {
        Map<String, Object> menu_data = new HashMap<String, Object>();
        Map<String, String> temp = matrixMenu.getMenu_data();
        if (CollectionUtils.isEmpty(temp)) { //兼容menu_data为null的脏数据
            return null;
        }
        menu_data.putAll(temp);
        List<MatrixMenu> childs = matrixMenu.getChild();
        if (CollectionUtils.isEmpty(childs)) {
            return menu_data;
        }
        List<Map<String, Object>> childItems = new ArrayList<Map<String, Object>>();
        menu_data.put("child", childItems);
        Map<String, Object> temp1 = null;
        for (MatrixMenu child : childs) {
            temp1 = this.adapteToWsParams(child);
            if (CollectionUtils.isEmpty(temp1)) {//兼容menu_data为null的脏数据
                continue;
            }
            childItems.add(temp1);
        }
        if (CollectionUtils.isEmpty(childItems)) {//兼容menu_data为null的脏数据
            menu_data.remove("child");
        }
        return menu_data;
    }

    @Override
    public MatrixMenu getMenuById(String menuId) {
        return this.repository.findOne(menuId);
    }

    public List<MatrixMenu> getMenuForWsAgent(List<String> reqIds, String id, String sys) {
        List<MatrixMenu> rs = new ArrayList<>();
        MatrixMenu rawMenu = this.getMenu(id, sys);
        if (null == rawMenu) {
            return rs;
        }
        if ("0".equalsIgnoreCase(rawMenu.getParent_id())) {
            rs = rawMenu.getChild();
        } else {
            rs.add(rawMenu);
        }
        return rs;
    }

    @Override
    public MatrixMenu getMenu(String id, String sys) {
        if (!StringUtils.isEmpty(sys)) {
            sys = sys.toUpperCase();
        }
        MatrixMenu root = null;
        if (StringUtils.isEmpty(id)) {
            root = this.repository.findRootBySys(sys);
            if (null == root) {
                return null;
            }
        } else {
            root = this.repository.findOne(id);
        }
        if (root == null) {
            return null;
        }
        this.setChildren(root);
        return root;
    }

    private void setChildren(MatrixMenu matrixMenu) {
        String parentId = matrixMenu.getId();
        String sys = matrixMenu.getSys();
        List<String> childrenId = matrixMenu.getChildren();
        if (!CollectionUtils.isEmpty(childrenId)) {
            List<MatrixMenu> childs = matrixMenu.getChild();
            if (CollectionUtils.isEmpty(childs)) {
                childs = new ArrayList<>();
                matrixMenu.setChild(childs);
            }
            for (String menuId : childrenId) {
                MatrixMenu child = this.repository.findByParentIdAndIdAndSysAndIsDel(parentId, menuId, sys, "1");
                if (null != child) {
                    setChildren(child);
                    childs.add(child);
                }
            }
        }
    }

    @Override
    public List<MatrixMenu> getAllMenuBySys(String sys) {
        return sortMenus(this.repository.findBySys(sys));
    }

    /**
     * 给menu排序
     *
     * @param list
     * @return
     */
    private List<MatrixMenu> sortMenus(List<MatrixMenu> list) {
        List<MatrixMenu> rtnL = new ArrayList<>();
        Map<String, List<MatrixMenu>> map = new HashMap<>();
        for (MatrixMenu menu : list) {
            if (map.containsKey(menu.getParent_id())) {
                map.get(menu.getParent_id()).add(menu);
            } else {
                List<MatrixMenu> l = new ArrayList<>();
                l.add(menu);
                map.put(menu.getParent_id(), l);
            }
        }
        for (String key : map.keySet()) {
            Collections.sort(map.get(key), new Comparator<MatrixMenu>() {
                @Override
                public int compare(MatrixMenu m1, MatrixMenu m2) {
                    String pid = m1.getParent_id();
                    List<String> list = pathContainer.getChild(pid);
                    int a = list.indexOf(m1.getId());
                    int b = list.indexOf(m2.getId());
                    return a - b;
                }
            });
            rtnL.addAll(map.get(key));
        }
        return rtnL;
    }

    @Override
    public void updateMenuChild(String menuId, String pageId, String type) {
        MatrixMenu menu = repository.findById(menuId);
        if ("remove".equals(type)) {
            menu.setChildren(ListUtil.removeChild(menu.getChildren(), pageId));
        } else if ("add".equals(type)) {
            menu.setChildren(ListUtil.addChild(menu.getChildren(), pageId));
        } else {
            logger.info("com.winit.menu.service.MatrixMenuService.updateMenuChild type = " + type);
            return;
        }
        operations.updateMenu(menu);
    }

    public void saveChildSort(String id, String sort) {
        MatrixMenu menu = repository.findById(id);
        if (null == menu) {
            logger.error("com.winit.menu.service.MatrixMenuService.saveChildSort id=" + id + ",sort=" + sort);
            return;
        }
        menu.setChildren(ListUtil.saveChild(sort));
        operations.updateMenu(menu);
    }

}
